#include "StdAfx.h"
#include "DrawDialog.h"
#include "UIFactory.h"
namespace UILib
{
    const SIZE szRoundCorner = {6, 6};

    CDrawDialog::CDrawDialog( const UINT uID ) : uID_(uID)\
        , DialogEvent_(NULL)\
        , CustomObserver_( NULL )\
        , MsgFilter_(NULL)\
        , DrawUITree_(NULL)
        , hOwner_(NULL)\
        , bInit_(false)\
        , bLostFocusClose(false)\
        , bTracking_(true)\
        , bNeedLayout_(false)\
        , clBackGround_(RGB(0X00, 0X00, 0X00))
        , bLayout_(false)
    {
        memset(&szOld, 0, sizeof(SIZE) );
    }

    CDrawDialog::~CDrawDialog( void )
    {
        m_hWnd = NULL;
    }
    void CDrawDialog::InitParam( const PDialogParam pParam )
    {
        if ( pParam )
        {
            hOwner_ = pParam->_hOwner;
            uID_ = pParam->_uID;
            bChild_ = pParam->_bChild;
            bLostFocusClose = pParam->_bLostFocusClose;
            memcpy(&rcCaption_, &pParam->_rcCaption, sizeof(RECT) );

            DialogEvent_ = pParam->_pEvent;
            MsgFilter_ = pParam->_pMsgFilter;

            DWORD styleValue = WS_CLIPCHILDREN | WS_CLIPSIBLINGS , dwExStyle = 0L;
            if ( CustomObserver_ )
            {
                CustomObserver_->GetWindowStytle( styleValue, dwExStyle );
            }
            this->Create(hOwner_, pParam->_rcWnd/*, wszUIWndName, 0L, 0L*/, wszUIWndName, styleValue, dwExStyle );
        }
    }

    void CDrawDialog::Show( const UINT uWSP, const bool bCenter /*= false */ )
    {
        if ( m_hWnd )
        {
            CWindow::ShowWindow(uWSP);
            if ( bCenter )
            {
                CWindow::CenterWindow(hOwner_);
            }
        }
        else
        {
            //assert(false);
        }
    }

    void CDrawDialog::Set_DialogEvent( const PDialogEvent pEvent )
    {
        DialogEvent_ = pEvent;
    }

    void CDrawDialog::Set_CustomObserver( const PCustomObserver pObserver )
    {
        CustomObserver_ = pObserver;
    }

    void CDrawDialog::Add_MessageFilter( const PUIMessageFilter pFilter )
    {
        MsgFilter_ = pFilter;
    }

    void CDrawDialog::Set_ID( const UINT uID )
    {
        uID_ = uID;
    }

    void CDrawDialog::Set_BackgroundColor( const COLORREF cl )
    {
        clBackGround_ = cl;
    }

    BOOL CDrawDialog::ProcessWindowMessage( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID /*= 0*/ )
    {
        BOOL bHandled = FALSE;
        switch (uMsg)
        {
        case WM_CREATE:         lResult = OnCreate(uMsg, wParam, lParam, bHandled);         break;
        case WM_CLOSE:          lResult = OnClose(uMsg, wParam, lParam, bHandled);          break;
        case WM_NCDESTROY:      lResult = OnNcDestroy(uMsg, wParam, lParam, bHandled);      break;
        case WM_PAINT:          lResult = OnPaint(uMsg, wParam, lParam, bHandled);          break;
        case WM_ERASEBKGND:     lResult = OnEraseBkgnd(uMsg, wParam, lParam, bHandled);     break;
        case WM_LBUTTONDOWN:    lResult = OnLbuttonDown(uMsg, wParam, lParam, bHandled);    break;
        case WM_LBUTTONUP:      lResult = OnLbuttonUp(uMsg, wParam, lParam, bHandled);      break;
        case WM_MOUSEMOVE:      lResult = OnMouseMove(uMsg, wParam, lParam, bHandled);      break;
        case WM_MOUSELEAVE:     lResult = OnMouseLeave(uMsg, wParam, lParam, bHandled);     break;
        case WM_SIZE:           lResult = OnSize(uMsg, wParam, lParam, bHandled);           break;
        case WM_NCHITTEST:      lResult = OnNcHitTest(uMsg, wParam, lParam, bHandled);      break;
        case WM_GETMINMAXINFO:  lResult = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled);  break;
        case WM_COMMAND:        lResult = OnCommand(uMsg, wParam, lParam, bHandled);        break;
        case WM_NOTIFY:         lResult = OnNotify(uMsg, wParam, lParam, bHandled);         break;
        case WM_TIMER:          lResult = OnTimer(uMsg, wParam, lParam, bHandled);          break;
        case WM_SETFOCUS:       lResult = OnSetFocus(uMsg, wParam, lParam, bHandled);       break;
        case WM_KILLFOCUS:      lResult = OnKillFocus(uMsg, wParam, lParam, bHandled);      break;
        default:
            break;
        }

        //handleMsg
        if ( DrawUITree_ )
        {
            BOOL bElemHandled = false;
            DrawUITree_->HandleMessage( uMsg, wParam, lParam, m_hWnd, bElemHandled );
        }

        if ( MsgFilter_ )
        {
            MsgFilter_->HandleMessage(uMsg, wParam, lParam, bHandled);
        }

        return bHandled;
    }
    LRESULT CDrawDialog::OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        DWORD styleValue = ::GetWindowLong(m_hWnd, GWL_STYLE);
        DWORD dwExStyle = ::GetWindowLong(m_hWnd, GWL_EXSTYLE);
        if ( CustomObserver_ )
        {
            CustomObserver_->GetWindowStytle( styleValue, dwExStyle );
        }
        if ( bLayout_ &&  (dwExStyle & WS_EX_LAYERED) != WS_EX_LAYERED )
        {
            dwExStyle = dwExStyle| WS_EX_LAYERED;
        }

        styleValue &= ~WS_CAPTION;
        ::SetWindowLong(m_hWnd, GWL_STYLE, styleValue);
        ::SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle );

        RECT rc = {0};
        GetWindowRect(&rc);
        if ( CustomObserver_ )
        {
            SIZE szRound = { 0 };
            if ( CustomObserver_->IsRoundConner() )
            {
                memcpy( &szRound, &szRoundCorner, sizeof(SIZE) );
            }
            HRGN Hrgn =CreateRoundRectRgn( 0 , 0, rc.right - rc.left + 1, rc.bottom - rc.top + 1, szRound.cx, szRound.cy);
            RECT rcClient = {0 , 0, rc.right - rc.left + 1, rc.bottom - rc.top + 1};
            CustomObserver_->GetRgn(Hrgn, rcClient );
            SetWindowRgn( Hrgn, TRUE );
            ::DeleteObject(Hrgn);
        }
        
        if ( bLayout_ )
        {
            UpdateWindow();
        }
        CUIFactory::Instance()->GetMessageDispatch()->AddObserver(dynamic_cast<RanderObserver*>(this));
        return S_OK;
    }
    LRESULT CDrawDialog::OnClose( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        CUIFactory::Instance()->GetMessageDispatch()->RemoveObserver(dynamic_cast<RanderObserver*>(this));
        if ( DialogEvent_ )
        {
            DialogEvent_->OnClose();
        }
        //m_hWnd = NULL;
        ::KillTimer(m_hWnd, 0x0FFF);
        return S_OK;
    }

    LRESULT CDrawDialog::OnNcDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        //
        CUIFactory::Instance();
        return S_OK;
    }

    LRESULT CDrawDialog::OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        PAINTSTRUCT ps = {0};
        ::BeginPaint(m_hWnd, &ps);
        Render(ps);
        ::EndPaint(m_hWnd, &ps);
        return S_OK;
    }

    LRESULT CDrawDialog::OnEraseBkgnd( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        bHandled = TRUE;
        return S_OK;
    }

    LRESULT CDrawDialog::OnMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        if(bTracking_)
        {
            TRACKMOUSEEVENT tme = {0};
            tme.cbSize          = sizeof(tme);
            tme.hwndTrack       = m_hWnd;
            tme.dwFlags         = TME_LEAVE | TME_HOVER;
            tme.dwHoverTime     = 1;
            bTracking_          = _TrackMouseEvent(&tme) ? true : false;
            bTracking_ = false;
        }
        else
        {
        }

        if ( DialogEvent_ )
        {
            POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
            DialogEvent_->OnMouseMove(pt);
        }
        return S_OK;
    }

    LRESULT CDrawDialog::OnMouseLeave( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        bTracking_ = true;
        if ( DialogEvent_ )
        {
            POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
            DialogEvent_->OnMouseMove(pt);
        }
        if ( DialogEvent_ )
        {
            DialogEvent_->OnMouseLeave();
        }
        return S_OK;
    }

    LRESULT CDrawDialog::OnLbuttonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        return S_OK;
    }

    LRESULT CDrawDialog::OnLbuttonUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        return S_OK;
    }

    LRESULT CDrawDialog::OnSize( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        if ( DialogEvent_ )
        {
            SIZE szWnd = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
            DialogEvent_->OnSize( szWnd );
        }
        bNeedLayout_ = true;
        return S_OK;
    }

    LRESULT CDrawDialog::OnNcHitTest( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        bHandled = TRUE;
        if ( CustomObserver_ )
        {
            POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
            RECT rect = { 0 };
            GetWindowRect( &rect );
            RECT rcCapton = {rect.left + rcCaption_.left, rect.top + rcCaption_.top, rect.left + rcCaption_.right, rect.top + rcCaption_.bottom};
            return CustomObserver_->NCHitTest( rect, rcCapton, pt );
        }
        else
        {
            return HTCLIENT;
        }
    }

    LRESULT CDrawDialog::OnGetMinMaxInfo( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        PMINMAXINFO pMinMaxInfo = ( PMINMAXINFO )lParam;
        if ( pMinMaxInfo )
        {
            if ( CustomObserver_ )
            {
                SIZE szMax = { 0 }, szMin = { 0 };
                bool bRet = CustomObserver_->MinMaxInfo( szMax, szMin );
                if ( bRet )
                {
                    if ( szMax.cx != 0 && szMax.cy != 0 )
                    {
                        pMinMaxInfo->ptMaxTrackSize.x = szMax.cx;
                        pMinMaxInfo->ptMaxTrackSize.y = szMax.cy;
                    }
                    if ( szMin.cx != 0 && szMin.cy != 0 )
                    {
                        pMinMaxInfo->ptMinTrackSize.x = szMin.cx;
                        pMinMaxInfo->ptMinTrackSize.y = szMin.cy;
                    }
                }
            }
        }
        return S_OK;
    }

    LRESULT CDrawDialog::OnCommand( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        return S_OK;
    }

    LRESULT CDrawDialog::OnNotify( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        return S_OK;
    }
    LRESULT CDrawDialog::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        if ( DialogEvent_ )
        {
            DialogEvent_->OnTimer( (UINT)wParam );
        }
        return S_OK;
    }

    LRESULT CDrawDialog::OnKillFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        if ( bLostFocusClose )
        {
            ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
        }
        return S_OK;
    }

    LRESULT CDrawDialog::OnSetFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
    {
        return S_OK;
    }

    void CDrawDialog::Render( const PAINTSTRUCT &ps )
    {
        RECT rect = {0};
        ::GetClientRect(m_hWnd, &rect);

        HDC hDC = ps.hdc;
        HDC hMemDC = CreateCompatibleDC(hDC);
        HBITMAP hBmpBkgnd = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);
        HBITMAP hOldBmpBkgnd = (HBITMAP)::SelectObject(hMemDC, hBmpBkgnd);

        if(ps.rcPaint.right > 0 || ps.rcPaint.bottom > 0)
        {
            //Draw bkg
            {
                HBRUSH hBrush = ::CreateSolidBrush( clBackGround_ );
                ::FillRect(hMemDC, &rect, hBrush);
                ::DeleteObject(hBrush);
            }

            if ( DrawUITree_ )
            {
                DrawUITree_->DrawElement(hMemDC, ps.rcPaint);
            }

        }

        ::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, hMemDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

        ::SelectObject(hMemDC, hOldBmpBkgnd);
        ::DeleteObject(hBmpBkgnd);
        ::DeleteObject(hMemDC);
    }

    void CDrawDialog::BindUITree( CDrawUITree* pUITree )
    {
        DrawUITree_ = pUITree;
    }

    CDrawView* CDrawDialog::GetChildrenByID( const UINT uID )
    {
        CDrawView* pView = NULL;
        if ( DrawUITree_ )
        {
            pView = DrawUITree_->GetChildrenByID(uID);
        }
        return pView;
    }

    void CDrawDialog::Set_Caption( const RECT rcCaption )
    {
        memcpy( &rcCaption_, &rcCaption, sizeof(RECT) );
    }

    void CDrawDialog::OnLayoutCheck()
    {
        if ( DrawUITree_ && IsWindow() )
        {
            RECT rcDraw = {0};
            bool bNeed = DrawUITree_->CheckInvalidateRect(rcDraw);
            if ( bNeed )
            {
                if ( bLayout_ )
                {
                    UpdateWindow();
                }
                else
                {
                    ::InvalidateRect(m_hWnd, &rcDraw, FALSE);
                }
            }

            if ( CustomObserver_ )
            {
                RECT rc = {0};
                GetWindowRect(&rc);

                SIZE szWnd = { rc.right - rc.left, rc.bottom - rc.top };
                if ( memcmp( &szOld, &szWnd, sizeof(SIZE) ) != 0 )
                {
                    SIZE szRound = { 0 };
                    if ( CustomObserver_->IsRoundConner() )
                    {
                        memcpy( &szRound, &szRoundCorner, sizeof(SIZE) );
                    }
                    HRGN Hrgn =CreateRoundRectRgn( 0 , 0, rc.right - rc.left + 1, rc.bottom - rc.top + 1, szRound.cx, szRound.cy);
                    RECT rcClient = {0 , 0, rc.right - rc.left + 1, rc.bottom - rc.top + 1};
                    CustomObserver_->GetRgn(Hrgn, rcClient );
                    SetWindowRgn( Hrgn, TRUE );
                    ::DeleteObject(Hrgn);

                    memcpy(&szOld, &szWnd, sizeof(SIZE) );
                }
            }

            if ( bNeedLayout_ )
            {
                RECT rcWnd = { 0 };
                GetWindowRect( &rcWnd );

                RECT rcClient = { 0, 0, rcWnd.right - rcWnd.left, rcWnd.bottom - rcWnd.top };
                DrawUITree_->Layout(rcClient);
                if ( bLayout_ )
                {
                    UpdateWindow();
                }
                else
                {
                    ::InvalidateRect(m_hWnd, &rcDraw, FALSE);
                }
                bNeedLayout_ = false;
            }
        }

    }

    void CDrawDialog::Set_LayoutAttr( const bool bLayout /*= false*/ )
    {
        bLayout_ = true;
    }

    void CDrawDialog::UpdateWindow()
    {
        HDC hDC = ::GetDC(m_hWnd);

        RECT rect = {0};
        ::GetWindowRect(m_hWnd, &rect);
        POINT ptWindowScreenPosition = {rect.left, rect.top};
        SIZE szWindow = {rect.right - rect.left, rect.bottom - rect.top};

        POINT clientPT= {0};
        memcpy(&clientPT, &ptWindowScreenPosition, sizeof(POINT));
        ::ScreenToClient(m_hWnd, &clientPT);

        RECT rcPaint = {clientPT.x, clientPT.y, clientPT.x + szWindow.cx, clientPT.y + szWindow.cy};


        HDC hMemDC = CreateCompatibleDC(hDC);
        COLORREF* pBackgroundBits;
        HBITMAP hBmpBkgnd = CreateMyBitmap(hDC, szWindow.cx, szWindow.cy, &pBackgroundBits);

        HBITMAP hOldBmpBkgnd = (HBITMAP)::SelectObject(hMemDC, hBmpBkgnd);
        {
            if ( DrawUITree_ )
            {
                DrawUITree_->DrawElement(hMemDC,rcPaint);
            }
        }
        BLENDFUNCTION blendPixelFunction= { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
        POINT ptSrc = {0, 0}; // start point of the copy from dcMemory to dcScreen

        // perform the alpha blend
        BOOL bRet= ::UpdateLayeredWindow(m_hWnd, hDC, &ptWindowScreenPosition, &szWindow, hMemDC,
            &ptSrc, 0, &blendPixelFunction, ULW_ALPHA);

        ::SelectObject(hMemDC, hOldBmpBkgnd);
        ::DeleteObject(hBmpBkgnd);
        ::DeleteObject(hMemDC);
        ::ReleaseDC(m_hWnd, hDC);
    }

    HBITMAP CDrawDialog::CreateMyBitmap( HDC hDC, int cx, int cy, COLORREF** pBits )
    {
        LPBITMAPINFO lpbiSrc = NULL;
        lpbiSrc = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFOHEADER)];
        if (lpbiSrc == NULL) return NULL;

        lpbiSrc->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        lpbiSrc->bmiHeader.biWidth = cx;
        lpbiSrc->bmiHeader.biHeight = cy;
        lpbiSrc->bmiHeader.biPlanes = 1;
        lpbiSrc->bmiHeader.biBitCount = 32;
        lpbiSrc->bmiHeader.biCompression = BI_RGB;
        lpbiSrc->bmiHeader.biSizeImage = cx * cy;
        lpbiSrc->bmiHeader.biXPelsPerMeter = 0;
        lpbiSrc->bmiHeader.biYPelsPerMeter = 0;
        lpbiSrc->bmiHeader.biClrUsed = 0;
        lpbiSrc->bmiHeader.biClrImportant = 0;

        HBITMAP hBitmap = CreateDIBSection (hDC, lpbiSrc, DIB_RGB_COLORS, (void **)pBits, NULL, NULL);
        ZeroMemory(*pBits, cx * cy * 4);
        delete []lpbiSrc;
        return hBitmap;
    }
}